<!--
 * @Author: aster lipian1004@163.com
 * @Date: 2025-02-20 17:11:32
 * @FilePath: \aster-flowable-uniapp\src\components\form\render\signature.vue
 * @Description: 签名
 * Copyright (c) 2025 by Aster, All Rights Reserved.
-->
<template>
  <van-field
    v-if="!_hidden"
    v-model="_value"
    :name="formItemProp"
    :readonly="true"
    :required="_required"
    :rules="_rules"
    :input-align="FieldInputAlignEnum.RIGHT"
    :error-message-align="FieldInputAlignEnum.RIGHT"
    :label-width="showLabel ? '' : '5rpx'"
    is-link
    @click="handleOpen"
  >
    <template #label>
      <text v-if="showLabel">{{ formItem.title }}</text>
    </template>
    <template #input>
      <van-image v-if="isNotEmpty(_value)" width="50" height="50" :src="_value" />
      <span v-else class="ph">
        {{ _placeholder }}
      </span>
    </template>
  </van-field>

  <van-popup
    v-model:show="showPicker"
    round
    position="bottom"
    :safe-area-inset-bottom="true"
    style="min-height: 50%"
  >
    <van-tabs v-model:active="activeTab">
      <van-tab title="签名" name="sign">
        <view class="sign-header" v-if="_showLineWidth || _showLineColor">
          <view class="sign-setting">
            <view class="sign-setting__width" v-if="_showLineWidth">
              <span>线宽</span>
              <uni-easyinput
                v-model="lineWidth"
                type="number"
                :clearable="false"
                style="width: 100rpx"
              />
            </view>
            <view class="sign-setting__color" v-if="_showLineColor">
              <span>颜色</span>
              <aster-color-picker @confirm="handleColorConfirm">
                <span
                  :style="{
                    backgroundColor: lineColor,
                    padding: '0 30rpx',
                  }"
                ></span>
              </aster-color-picker>
            </view>
          </view>
        </view>
        <van-image
          v-if="_showImage"
          width="100%"
          height="100%"
          style="padding: 16rpx; border: 1px dotted #dadada"
          :src="_value"
        />
        <van-signature
          ref="signRef"
          v-else
          :line-width="_lineWidth"
          :pen-color="lineColor"
          @submit="onSubmit"
          @clear="onClear"
        />
        <view class="draw-button" v-if="_showImage">
          <van-button
            type="default"
            size="small"
            class="draw-button__margin"
            @click="showPicker = false"
          >
            取消
          </van-button>
          <van-button
            type="primary"
            size="small"
            class="draw-button__margin"
            @click="drawCanvas(true)"
          >
            重绘
          </van-button>
        </view>
      </van-tab>
      <van-tab title="本地签名" name="local" v-if="_showLocal">
        <view class="p-40rpx">
          <van-uploader
            v-model="images"
            :multiple="false"
            :max-count="1"
            :max-size="ImageUpload.maxSize * 1024 * 1024"
            accept=""
            upload-text="选择附件"
            :disabled="_readonly"
            :before-read="beforeRead"
            :after-read="afterRead"
            @delete="deleteFile"
          >
            <template #preview-cover="{ file }">
              <div class="preview-cover van-ellipsis">{{ file.name }}</div>
            </template>
          </van-uploader>
        </view>

        <aster-tabbar
          ref="userTabbarRef"
          :customStyle="{
            borderTopLeftRadius: '10px',
            borderTopRightRadius: '10px',
          }"
        >
          <van-button type="primary" block round @click="handleLocalConfirm">确定选择</van-button>
        </aster-tabbar>
      </van-tab>
    </van-tabs>
  </van-popup>
</template>
<script setup lang="ts">
  import { evaluateFormula } from '@/utils/workflow';
  import { computed, nextTick, PropType, ref } from 'vue';
  import { isNotEmpty, isString } from '@/utils/is';
  import { FormPermissionEnum } from '@/enums/workFlowEnum';
  import { FieldInputAlignEnum } from '@/enums/formEnum';
  import AsterTabbar from '@/components/common/aster-tabbar.vue';
  import AsterColorPicker from '@/components/common/aster-color-picker/index.vue';
  import type { SignatureInstance } from 'vant';
  import { ImageUpload } from '@/utils/config/fileConfig';
  import { uploadFile } from '@/utils/fileUtils';
  import { Toast } from '@/utils/uniapi/prompt';
  import { onLoad } from '@dcloudio/uni-app';

  const emit = defineEmits(['update:value']);
  const props = defineProps({
    value: {
      type: String,
      default: '',
    },
    mode: {
      type: String as PropType<'design' | 'form' | 'search' | 'table' | 'print'>,
      default: 'design',
    },
    formData: {
      type: Object as PropType<WorkForm.FormDataModel>,
      default: {},
    },
    formItem: {
      type: Object as PropType<WorkComponent.ComponentConfig>,
      default: {},
    },
    tableId: {
      type: String,
      default: '',
    },
    tableIndex: {
      type: Number,
      default: 0,
    },
    showLabel: {
      type: Boolean,
      default: true,
    },
  });

  // 当前激活的tab
  const activeTab = ref('sign');
  // 线宽和颜色
  const lineWidth = ref(3);
  const lineColor = ref('#000000');
  // 显示弹出层
  const showPicker = ref(false);
  // 注册组件
  const signRef = ref<SignatureInstance>();
  // 是否重绘
  const drawReset = ref(false);
  // 附件
  const images = ref<any>([]);
  const imageUrl = ref('');

  // 只显示图片
  const _showImage = computed(() => {
    return isNotEmpty(_value.value) && (!drawReset.value || _readonly.value);
  });

  /**
   * @description: 重置画布
   */
  const drawCanvas = (type: boolean) => {
    drawReset.value = type;
    nextTick(() => {
      if (signRef.value) {
        const e = signRef.value.$el;
        const content = e.childNodes[0];
        const canvas = e.childNodes[0].childNodes[0];
        canvas.width = content.clientWidth;
        canvas.height = content.clientHeight;
      }
    });
  };

  /**
   * @description: 颜色选择
   */
  const handleColorConfirm = (color) => {
    lineColor.value = color;
  };

  /**
   * @description: 提交签名
   */
  const onSubmit = (data) => {
    _value.value = data.image;
    showPicker.value = false;
  };

  /**
   * @description: 清除签名
   */
  const onClear = () => {
    _value.value = '';
    drawCanvas(true);
  };

  // 打开签名弹出层
  const handleOpen = () => {
    if (!_readonly.value) {
      showPicker.value = true;
      drawCanvas(false);
    }
  };

  /**
   * @description: 上传前校验
   * @param {any} rawFile 附件
   * @return {*}
   */
  const beforeRead = (rawFile) => {
    // 判断附件类型
    if (!ImageUpload.type.includes(rawFile.type)) {
      Toast('上传签名的格式不符合要求');
      return false;
    } else if (rawFile.size / 1024 / 1024 > ImageUpload.maxSize) {
      // 判断附件大小
      Toast('上传签名大小不能超过' + ImageUpload.maxSize + 'MB');
      return false;
    }
    return true;
  };

  /**
   * @description: 上传文件
   * @param {*} rawFile
   * @return {*}
   */
  const afterRead = (rawFile: any) => {
    if (Array.isArray(rawFile)) {
      rawFile.forEach((file) => {
        handleUploadFile(file);
      });
    } else {
      handleUploadFile(rawFile);
    }
  };

  /**
   * 删除附件
   * @param file 附件
   * @param index 索引
   */
  const deleteFile = () => {
    imageUrl.value = '';
  };

  /**
   * 上传附件
   * @param file 附件
   */
  const handleUploadFile = (rawFile: any) => {
    uploadFile(rawFile.file, (data) => {
      imageUrl.value = data.url;
    });
  };

  /**
   * @description: 确定本地签名
   */
  const handleLocalConfirm = () => {
    _value.value = imageUrl.value;
    showPicker.value = false;
  };

  /**
   * @description: 线宽
   */
  const _lineWidth = computed(() => {
    if (lineWidth.value) {
      if (
        isString(lineWidth.value) &&
        /^[+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(lineWidth.value)
      ) {
        return parseInt(lineWidth.value);
      }
      return lineWidth.value;
    }
    return 3;
  });

  /**
   * @description: 是否显示线宽
   */
  const _showLineWidth = computed(() => {
    return props.formItem.props.showLineWidth;
  });

  /**
   * @description: 是否显示颜色
   */
  const _showLineColor = computed(() => {
    return props.formItem.props.showLineColor;
  });

  /**
   * @description: 是否显示本地签名
   */
  const _showLocal = computed(() => {
    return props.formItem.props.showLocal;
  });

  // 键
  const formItemProp = computed(() => {
    if (isNotEmpty(props.tableId)) {
      return props.tableId + '.' + props.tableIndex + '.' + props.formItem.id;
    } else {
      return props.formItem.id;
    }
  });

  /**
   * @description: 组件值
   */
  const _value = computed({
    get() {
      return props.value;
    },
    set(val) {
      emit('update:value', val);
    },
  });

  /**
   * @description: 提示词
   */
  const _placeholder = computed(() => {
    if (_readonly.value) {
      return '';
    }
    return isNotEmpty(_value.value) ? '' : '请签名';
  });

  /**
   * @description: 是否必填, true-必填
   */
  const _required = computed(() => {
    return props.formItem.props.required;
  });

  /**
   * @description: 是否只读, true-只读
   */
  const _readonly = computed(() => {
    let r = props.formItem.props.readonly;
    if (props.formItem.operation && props.formItem.operation.length > 0) {
      r = r || props.formItem.operation[0] == FormPermissionEnum.READONLY;
    }
    return r;
  });

  /**
   * @description: 校验规则
   */
  const _rules = computed(() => {
    const isRequired = _required.value;
    if (props.mode != 'form') {
      return [];
    }
    return isRequired ? [{ required: true, message: `请输入${props.formItem.title}` }] : [];
  });

  /**
   * @description: 是否隐藏, true-隐藏
   */
  const _hidden = computed(() => {
    let r = false;
    // 解析隐藏条件公式
    if (props.formItem.props.hidden) {
      let expression = props.formItem.props.hidden;
      // 如果是子表中的控件，则需要用到下标
      if (isNotEmpty(props.tableId)) {
        expression = expression.replaceAll('?', props.tableIndex);
      }
      r = evaluateFormula(expression, props.formData);
    }
    // 判断流程节点下该控件是否隐藏
    if (props.formItem.operation && props.formItem.operation.length > 0) {
      r = r || props.formItem.operation[0] == FormPermissionEnum.HIDDEN;
    }
    return r;
  });

  onLoad(() => {
    if (isNotEmpty(_value.value)) {
      imageUrl.value = _value.value;
      images.value = [
        {
          url: _value.value,
        },
      ];
    }
  });

  defineExpose({
    _hidden,
  });
</script>
<style scoped lang="scss">
  .sign-header {
    padding: 30rpx 10rpx;
  }
  .sign-setting {
    display: flex;
    justify-content: space-around;
    align-items: center;
    span {
      padding-right: 10px;
    }

    &__width {
      display: flex;
      justify-content: center;
      align-items: center;
    }

    &__color {
      display: flex;
    }
  }

  .draw-button {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 0 16rpx;

    &__margin {
      padding: 0 32rpx;
      margin-top: 16rpx;
      margin-left: 16rpx;
    }
  }

  .preview-cover {
    position: absolute;
    bottom: 0;
    box-sizing: border-box;
    width: 100%;
    padding: 4px;
    color: #fff;
    font-size: 12px;
    text-align: center;
    background: rgba(0, 0, 0, 0.3);
  }
</style>
